<?php
/**
 * Display a psychometric analysis of the quiz, questions and attempts
 * - Quiz stats
 * - Score distribution graphic
 * - Questions stats and details
 * - Attempts stats
 * NOTES : Only multichoice questions (single answer) are analyzed in this report
 *
 * @author Juan Antonio Andres (juanantonio.andres@uam.es)
 * @author Antonio Espinosa (aespinosa@teachnova.com)
 * @author Roberto Fernandez
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @package quiz
 */

require_once($CFG->libdir.'/tablelib.php');

define('QUIZ_HIGHESTATTEMPT', 0);
define('QUIZ_FIRSTATTEMPT', 1);
define('QUIZ_LASTATTEMPT', 2);

define('QUIZ_REPORT_PRECISION', 2);        // All values with n decimal precision
define('QUIZ_REPORT_CUTPERCENT', 27);      //
define('QUIZ_REPORT_TRUSTINTERVAL', 1.96); // z=1.96 for 95% trust percent

class quiz_report extends quiz_default_report {

    var $name           = "-- Unknown --";     // Name of the quiz
    var $attemptscount  = 0;      // Number of attempts
    var $questionscount = 0;      // Number of questions

    var $choicescountavg        = "Nc";  // Choices number average
    var $successavg             = "Nc";  // Quiz success average
    var $successcorrection      = "Nc";  // Quiz success correction
    var $variation              = "Nc";  // Quiz variation
    var $standarddeviation      = "Nc";  // Quiz standard deviation
    var $biserialcorrelation    = "Nc";  // Quiz biserial correlation
    var $successavgproportion   = "Nc";  // Quiz success average proportion
    var $alphacronbach          = "Nc";  // Quiz Cronbach alpha

    var $questions  = array();   // Questions
    // id               - Question id
    // name             - Question name
    // grade            - Question value in quiz (1 point?)
    // success          - Number of attempts that success this question
    // error            - Number of attempts that fail this question
    // leftblank        - Number of attempts that left blank this question
    // easilyindex              - Question easily index
    // discriminationindex      - Question discimination index
    // variation                - Question variation
    // standarddeviation        - Question standard deviation
    // biserialcorrelation      - Question biserial correlation
    // alphacronbach            - Quiz Cronbach alpha without this question
    // quizdeviationwithoutme   - Quiz deviation without this question
    // quizsuccessavgwithoutme  - Quiz success average without this question
    // choices          - Question choices
    //    id            - Choice id
    //    answer        - Choice text
    //    credit        - Choice credit
    //    number        - Number of attempts that mark this question
    //    users                 - Array with userids of students that mark this question
    //    correct       - 0 if incorrect, 1 if correct

    var $attempts = array();   // Attempts
    // id               - Attempt id
    // userid           - User id that sent the attempt
    // name             - User firstname and lastname
    // score            - Attempt total score considering choice credit
    // success          - Number of questions with success answer
    // error            - Number of questions with error answer
    // leftblank        - Number of questions left blank
    // successpercent       - Percent of questions with success answer
    // successtotal         - Number of questions with success answer
    // successcorrection    - Attempt success correction
    // trustintervallow     - Attempt trust interval (low value)
    // trustintervalhigh    - Attempt trust interval (high value)
    // successquestions     - Array of question (qid - answer) with success answer
    // errorquestions       - Array of question (qid - answer) with error answer
    // leftblankquestions   - Array of question ids left blank



    /**
     * Display the report.
     *
     * @param object   $quiz      Quiz to analyze
     * @param object   $cm        Course module, to know if groups are being in use
     * @param object   $course    Course, to print header information
     *
     * @return boolean
     */
    function display($quiz, $cm, $course) {
        global $CFG, $SESSION, $QTYPES;

        // 1. Print header and get options
        $strnoattempts = get_string('noattempts','quiz');

        // Show header tabs
        $this->print_header_and_tabs($cm, $course, $quiz, $reportmode="psychometric");

        // If there are no questions, just finish
        if (!$quiz->questions) {
            print_heading($strnoattempts);
            return true;
        }

        $pageoptions = array();
        $pageoptions['id']   = $cm->id;
        $pageoptions['q']    = $quiz->id;
        $pageoptions['mode'] = 'psychometric';

        $reporturl = new moodle_url($CFG->wwwroot.'/mod/quiz/report.php', $pageoptions);

        // Check to see if groups are being used in this quiz
        $currentgroup = groups_get_activity_group($cm, true);
        if ($groupmode = groups_get_activity_groupmode($cm)) {
//            groups_print_activity_menu($cm, $CFG->wwwroot . "/mod/quiz/report.php?id=$cm->id&amp;mode=psychometric");
            groups_print_activity_menu($cm, $reporturl->out());
        }

        // In this analysis we only select one attempt for aech student
        // Allow different attempts selection policies:
        //   QUIZ_HIGHESTATTEMPT (default) - Select only the highest attempts
        //   QUIZ_FIRSTATTEMPT             - Select only the first attempts
        //   QUIZ_LASTATTEMPT              - Select only the last attempts
        $attemptselection = QUIZ_HIGHESTATTEMPT;

        // Get selection form $SESSION
        if(!isset($SESSION->quiz_psychometric_table)) {
            $SESSION->quiz_psychometric_table = array('attemptselection' => 0, 'attemptorder' => 'lastname_asc');
        }

        // Load variables:
        foreach($SESSION->quiz_psychometric_table as $option => $value) {
            $urlparam = optional_param($option, NULL, PARAM_INT);
            if($urlparam === NULL) {
                $$option = $value;
            } else {
                $$option = $SESSION->quiz_psychometric_table[$option] = $urlparam;
            }
        }

        // 2. Load data
        // If there are no data to analysis, just finish
        $errorstr = $this->load_data($quiz, $attemptselection, $currentgroup);
        if (!empty($errorstr)) {
            print_heading(get_string($errorstr));
            return true;
        }

        echo '<script>
                function abrir(file) {
                        window.open(\'../../help.php?file=\' + file + \'&module=quiz\',\'\',\'resizable=yes,toolbar=no,status=no,scrollbars=yes,menubar=no,width=640,height=480\');
                }

                function mostrardiv(id) {
                        div = document.getElementById(id);
                        if(div.style.display == "")
                                div.style.display=\'none\';
                        else
                                div.style.display = "";
                }
              </script>';

        echo '<script type="text/javascript" src="report/psychometric/js/jquery.min.js"></script>';
        echo '<script type="text/javascript" src="report/psychometric/js/jquery-ui.min.js"></script>';
        echo '<script type="text/javascript" src="report/psychometric/js/main.js"></script>';
        echo '<link rel="stylesheet" type="text/css" href="report/psychometric/css/jquery-ui-1.8.9.custom.css">';

         // Show page info
        $this->display_info();

        // Show attempts stats list
        $this->display_attempts_table();

        // Show questions stats list and details hidden
        $this->display_questions_table();

        // 3. Show quiz stats
        $this->display_quiz_table();

        // 4. Show graphics

        $_SESSION['question'] = $this->questions;
        $this->display_graphics($quiz);

        return true;
    }

    /**
     * Display the options form.
     *
     * @param object   $quiz      Quiz to analyze
     * @param object   $cm        Course module, to know if groups are being in use
     * @param integer  $attemptselection    Attempt policy selection
     *
     * @return void
     */
    function display_options_form($quiz, $cm, $attemptselection, $urlroot) {
        global $CFG, $USER;
        $options = array ( QUIZ_HIGHESTATTEMPT => get_string("attemptshighest", 'quiz_psychometric'),
                           QUIZ_FIRSTATTEMPT => get_string("attemptsfirst", 'quiz_psychometric'),
                           QUIZ_LASTATTEMPT  => get_string("attemptslast", 'quiz_psychometric'));
        $output = popup_form($urlroot.'&amp;attemptselection=', $options, 'options',
                             $attemptselection, '', '', '', true, 'self',
                             get_string('attemptselection', 'quiz_psychometric'));
        $output = '<div class="groupselector">'. $output .'</div>';
        echo '<div class="groupselector">&nbsp;&nbsp;&nbsp;</div>';
        echo $output;
        echo '<div class="clearer"></div>';
    }

    /**
     * Display the score chart of the quiz.
     *
     * @return void
     */
    function display_quiz_chart($quiz) {
        global $CFG;
        $imageurl = $CFG->wwwroot.'/mod/quiz/report/psychometric/psychometricgraph.php?id='.$quiz->id;
        return '<img src="'.$imageurl.'" alt="'.get_string('psychometricgraph', 'quiz_psychometric').'" style="max-width:360px"/>';

      }

    /**
     * Display the facility chart of the quiz.
     *
     * @return void
     */
    function display_facility_chart() {
        global $CFG;
        $imageurl = $CFG->wwwroot.'/mod/quiz/report/psychometric/facilitygraph.php';
        return '<img src="'.$imageurl.'" alt="'.get_string('facilitygraph', 'quiz_psychometric').'" style="max-width:360px"/>';

      }

    /**
     * Display the choice chart of the quiz.
     *
     * @return void
     */
    function display_choice_chart() {
        global $CFG;

        $imageurl = $CFG->wwwroot.'/mod/quiz/report/psychometric/choicegraph.php';
        return '<img src="'.$imageurl.'" alt="'.get_string('choicegraph', 'quiz_psychometric').'" style="max-width:360px"/>';
      }

    /**
     * Display the discrimination chart of the quiz.
     *
     * @return void
     */
      function display_info() {
          global $CFG;

          echo '<h2 class="main">'.get_string('pageinfo', 'quiz_psychometric').'</h2>';
          echo '<center>';
          $imagetext = '<img class="iconhelp" alt="'.s(strip_tags($tooltip)).'" width="16" height="16" src="'.$CFG->modpixpath.'/quiz/pix/pr.png" />';
          print_string('fiability', 'quiz_psychometric');
          helpbutton("fiabilityrec", get_string("oficialrec","quiz_psychometric"), "quiz", true, false, '', false, $imagetext);
          echo '<br>';
          print_string('validity', 'quiz_psychometric');
          helpbutton("validityrec", get_string("oficialrec","quiz_psychometric"), "quiz", true, false, '', false, $imagetext);
          echo '</center>';

          echo '<p style="padding-top:20px; margin-left:40px;margin-right:40px;">'.get_string('diline1', 'quiz_psychometric').'</p>';
          echo '<p style="margin-left:40px;margin-right:40px;">'.get_string('diline2', 'quiz_psychometric').'</p>';
          echo '<ul style="margin-left:40px;margin-right:40px;">'.get_string('diline3', 'quiz_psychometric').'</ul>';

          echo '<p style="margin-left:40px;margin-right:40px;">'.get_string('diline4', 'quiz_psychometric').'</p>';
          echo '<p style="margin-left:60px;">'.get_string('diline5', 'quiz_psychometric').'</p>';
          echo '<p style="margin-left:40px;margin-right:40px;">'.get_string('diline6', 'quiz_psychometric').' <a style="text-decoration:none;color:red"><b>'.get_string('diline7', 'quiz_psychometric').'</b></a> '.get_string('diline8', 'quiz_psychometric').'</p>';

          echo '<hr align="CENTER" size="2" width="70%" noshade>';
      }

    /**
     * Display the discrimination chart of the quiz.
     *
     * @return void
     */
    function display_discrimination_chart() {
        global $CFG;

        $imageurl = $CFG->wwwroot.'/mod/quiz/report/psychometric/discriminationgraph.php';
        return '<img src="'.$imageurl.'" alt="'.get_string('discriminationgraph', 'quiz_psychometric').'" style="max-width:360px"/>';
      }

    /**
     * Display stats of the quiz.
     *
     * @return void
     */
    function display_quiz_table() {
        $columns = array();
        $headers = array();

        $columns[] = 'quiz';
        $headers[] = get_string('quiz', 'quiz_psychometric');
        $columns[] = 'nquestions';
        $headers[] = get_string('nquestions', 'quiz_psychometric');
        $columns[] = 'successaverage';
        $headers[] = get_string('successaverage', 'quiz_psychometric');
        $columns[] = 'successaverageproportion';
        $headers[] = get_string('successaverageproportion', 'quiz_psychometric');
        $columns[] = 'successcorrection';
        $headers[] = get_string('successcorrection', 'quiz_psychometric');
        $columns[] = 'standarddeviation';
        $headers[] = get_string('standarddeviation', 'quiz_psychometric');
        $columns[] = 'biserialcorrelation';
        $headers[] = get_string('biserialcorrelationavg', 'quiz_psychometric');
        $columns[] = 'alphacronbach';
        $headers[] = get_string('alphacronbach', 'quiz_psychometric');

        $table = new flexible_table('mod-quiz-report-psychometric-quiz');

        $table->define_columns($columns);
        $table->define_headers($headers);

        $table->set_attribute('cellspacing', '0');
        $table->set_attribute('id', 'quiz');
        $table->set_attribute('class', 'generaltable generalbox');
        $table->set_attribute('style','text-align:center;');

        $table->setup();

        echo '<script>
                $(document).ready(function() {
                        $(\'#quizanalysis\').dialog({
                                                        autoOpen: false,
                                                        width:900,
                                                        height:400,
                                                        minWidth:300,
                                                        title: \''.$this->name.'\',
                                                        modal: true
                                                });

                        $(\'#ba\').click(function() {
                                                $(\'#quizanalysis\').dialog(\'open\');
                                                return false;
                                        });

                        $(\'a[href*=#]\').click(function() {
                                return false;
                                        });
                });
               </script>';

        $row = array();
        $row[] = '<a id="ba" href="#">'.$this->name.'</a>';
        $row[] = $this->questionscount;
        $row[] = get_float_precision($this->success_average());

        if  ($this->success_average_proportion() >= 0.85)
               $row[]= '<a class="screenshot" rel="report/psychometric/img/tooeasyquiz.png" style="font-weight:bold;background-color:red;text-decoration:none">'.get_percent_precision($this->success_average_proportion()).'</a>';
        else if ($this->success_average_proportion() <= 0.15)
               $row[]= '<a class="screenshot" rel="report/psychometric/img/toohardquiz.png" style="font-weight:bold;background-color:red;text-decoration:none">'.get_percent_precision($this->success_average_proportion()).'</a>';
        else
               $row[] = get_percent_precision($this->success_average_proportion());

        $row[] = get_float_precision($this->success_correction());
        $row[] = get_float_precision($this->standard_deviation());

        if  ($this->biserial_correlation() >= 0.10)
               $row[] = get_float_precision($this->biserial_correlation());
        else
               $row[] = '<a style="font-weight:bold;background-color:red;text-decoration:none">'.get_float_precision($this->biserial_correlation()).'</a>';

        if ($this->alpha_cronbach() >= 0.6)
               $row[] = get_float_precision($this->alpha_cronbach());
        else
               $row[] = '<a style="font-weight:bold;background-color:red;text-decoration:none">'.get_float_precision($this->alpha_cronbach()).'</a>';

        $table->add_data($row);

        echo '<h2 class="main">'.get_string('quiztabletitle', 'quiz_psychometric').'</h2>';
        $table->print_html();

        // Hidden div with quiz analysis
        $this->display_quiz_info();
    }

    /**
     * Quiz Info Dialog
     *
     * @return void
     */
    function display_quiz_info() {
        echo '<div id="quizanalysis" style="display:none;">';
        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('dqiline1', 'quiz_psychometric').' '.$this->name.' '.get_string('dqiline2', 'quiz_psychometric').' '.$this->questionscount.' '.get_string('dqiline3', 'quiz_psychometric').' '.get_float_precision($this->success_average());
        echo get_string('dqiline4', 'quiz_psychometric').' '.get_percent_precision($this->success_average_proportion()).'. '.get_string('dqiline5', 'quiz_psychometric');

        if (get_percent_precision($this->success_average_proportion()) >= 0.85)
            echo '<b><a style="color:red;text-decoration:none;">'.get_string('tooeasy', 'quiz_psychometric').'</a></b>';

        if (0.61 <= get_percent_precision($this->success_average_proportion()) && get_percent_precision($this->success_average_proportion()) < 0.85)
            echo '<b><a style="color:red;text-decoration:none;">'.get_string('easy', 'quiz_psychometric').'</a></b>';

        if (0.4 <= get_percent_precision($this->success_average_proportion()) && get_percent_precision($this->success_average_proportion()) < 0.61)
            echo '<b><a style="color:red;text-decoration:none;">'.get_string('difficult', 'quiz_psychometric').'</a></b>';

        if (0.16 <= get_percent_precision($this->success_average_proportion()) && get_percent_precision($this->success_average_proportion()) < 0.4)
            echo '<b><a style="color:red;text-decoration:none;">'.get_string('verydifficult', 'quiz_psychometric').'</a></b>';

        if (get_percent_precision($this->success_average_proportion()) <= 0.15)
            echo '<b><a style="color:red;text-decoration:none;">'.get_string('toodifficult', 'quiz_psychometric').'</a></b>';

        echo '</p>';
        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('dqiline6', 'quiz_psychometric').'</p>';
        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('dqiline7', 'quiz_psychometric').get_float_precision($this->success_correction()).'. '.get_string('dqiline8', 'quiz_psychometric').'</p>';

        $diferences=0;
        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('dqiline9', 'quiz_psychometric').get_float_precision($this->standard_deviation()).'. '.get_string('dqiline10', 'quiz_psychometric').get_float_precision($this->biserial_correlation()).' '.get_string('dqiline11', 'quiz_psychometric');

        if ($this->biserial_correlation() >= 0.1)
        {
            $diferences=1;
            echo '<b><a style="color:red; text-decoration:none;">'.get_string('differs', 'quiz_psychometric').'</a></b>';
        }
        else
            echo '<b><a style="color:red; text-decoration:none;">'.get_string('nodiffers', 'quiz_psychometric').'</a></b>';

        echo get_string('dqiline12', 'quiz_psychometric').'</p>';

        if($diferences==1)
        {
            echo '<p style="padding-left:5%">'.get_string('dqiline13', 'quiz_psychometric').'</p>';

            if( $this->biserial_correlation() >= 0.3)
                    echo '<p style="font-size:0.75em; padding-left:20px; padding-right:20px;>'.get_string('dqiline14', 'quiz_psychometric').'</p>';
        }
        else
        {
          if( $this->biserial_correlation() >= 0)
             echo '<p>'.get_string('dqiline15', 'quiz_psychometric').'</p>';
          else
             echo '<p>'.get_string('dqiline16', 'quiz_psychometric').'</p>';

          echo '<p>'.get_string('dqiline17', 'quiz_psychometric').'</p>';
        }

        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('dqiline18', 'quiz_psychometric').get_float_precision($this->alpha_cronbach());

        if( $this->alpha_cronbach() >= 0.6)
        {
            echo get_string('dqiline19', 'quiz_psychometric');

            if( $this->alpha_cronbach() <= 0.75)
               echo '<b><a style="color:red; text-decoration:none;">'.get_string('good', 'quiz_psychometric').'</a></b></p>';
            else
               echo '<b><a style="color:red; text-decoration:none;">'.get_string('verygood', 'quiz_psychometric').'</a></b></p>';

            echo '<p style="padding-left:5%">'.get_string('dqiline20', 'quiz_psychometric').'</p>';
        }
        else
        {
            echo '.</p>';
            echo '<p style="padding-left:5%">'.get_string('dqiline21', 'quiz_psychometric').'</p>';
        }
        echo '</div>';
    }

    /**
     * Display questions stats.
     *
     * @return void
     */

    function display_questions_table() {
        global $CFG;
        echo '<h2 class="main">'.get_string('questiontabletitle', 'quiz_psychometric').'</h2>';
        echo '<p style="margin-left:40px;margin-right:40px;">'.get_string('dqtline1', 'quiz_psychometric').'</p>';
        echo '<table id="questions" class="flexible generaltable generalbox" style="text-align:center;"><tbody><tr>';
        echo '<th class="header c0">'.get_string('question', 'quiz_psychometric').'</td>';
        echo '<th class="header c1">'.get_string('nchoices', 'quiz_psychometric').'</th>';
        echo '<th class="header c2">'.get_string('easilyindex', 'quiz_psychometric').'</th>';
        echo '<th class="header c3">'.get_string('discriminationindex', 'quiz_psychometric').'</th>';
        echo '<th class="header c4">'.get_string('standarddeviation', 'quiz_psychometric').'</th>';
        echo '<th class="header c5">'.get_string('biserialcorrelation', 'quiz_psychometric').'</th>';
        echo '<th class="header c6">'.get_string('alphacronbach', 'quiz_psychometric').'</th>';
        echo '<th class="header c7">'.get_string('seeoptions', 'quiz_psychometric').'</th>';
        $rowindex = 0;
        $count = 0;
        foreach ($this->questions as $i) {
            $count++;
            $rowindex = ($rowindex == 0) ? 1 : 0;
            // Print Script for Modal Dialog
            echo '<script>
                        $(document).ready(function() {
                                $(\'#q'.$count.'\').dialog({
                                                                autoOpen: false,
                                                                width:900,
                                                                height:400,
                                                                minWidth:300,
                                                                title: \''.$i->name.'\',
                                                                modal: true
                                                        });
                                $(\'#b'.$count.'\').click(function() {
                                                        $(\'#q'.$count.'\').dialog(\'open\');
                                                        return false;
                                                });
                        });
                  </script>';

            //Warning message: style="font-weight:bold;color:red"
            echo '</tr><tr class="r'.$rowindex.'">';
            echo '<td class="cell c0"><a id="b'.$count.'" href="#">'. $i->name .'</a></td>';
            echo '<td class="cell c1">'. $i->choicescount ."</td>";
            echo '<td class="cell c2"';

            if ( $this->question_easily_index($i) >= 0.9)
               echo 'style="font-weight:bold;background-color:red"><a class="screenshot" rel="report/psychometric/img/tooeasy.png" style="text-decoration:none;">';
            else if ( $this->question_easily_index($i) <= 0.15)
               echo 'style="font-weight:bold;background-color:red"><a class="screenshot" rel="report/psychometric/img/toohard.png" style="text-decoration:none;">';
            else
               echo '>';

            echo get_float_precision($this->question_easily_index($i));
            //echo ' ('.$i->success.'/'.$this->attemptscount.')';  // Debug

            if (($this->question_easily_index($i) >= 0.9) || ($this->question_easily_index($i) <= 0.15))
               echo '</a>';
            echo '</td>';

            if ($this->question_discrimination_index($i) >= 0.1)
               echo '<td class="cell c3">'. get_float_precision($this->question_discrimination_index($i));
            else
               echo '<td class="cell c3" style="text-align:center;">'. get_float_precision($this->question_discrimination_index($i));

            //$basp = attempts_success_proportion($this->best_attempts(QUIZ_REPORT_CUTPERCENT), $i); // Debug
            //$wasp = attempts_success_proportion($this->worst_attempts(QUIZ_REPORT_CUTPERCENT), $i); // Debug
            //echo ' ('.$basp.'-'.$wasp.')';  // Debug
            echo '</td>';
            echo '<td class="cell c4">'. get_float_precision($this->question_standard_deviation($i)) .'</td>';

            if ($this->question_biserial_correlation($i) >= 0.2)
               echo '<td class="cell c5">'. get_float_precision($this->question_biserial_correlation($i)) .'</td>';
            else
               echo '<td class="cell c5" style="font-weight:bold;background-color:red">'. get_float_precision($this->question_biserial_correlation($i)) .'</td>';

            if ($this->alpha_cronbach() > $this->question_alpha_cronbach($i))
               echo '<td class="cell c6">'. get_float_precision($this->question_alpha_cronbach($i)) .'</td>';
            else
               echo '<td class="cell c6" style="font-weight:bold;background-color:red">'. get_float_precision($this->question_alpha_cronbach($i)) .'</td>';

            echo '<td class="cell c7" style="text-align:center;"><a href="javascript:mostrardiv(\'optiont'.$count.'\');"><img src="'.$CFG->modpixpath.'/quiz/pix/pl.png"></a></td>';

            // Hidden div with question analysis
            $this->display_question_info($i,$count,$choice);

            // Show options stats for each question when avaible
            $this->display_option_table($count,$i);
        }
        echo "</tr></tbody></table>";
    }

    /**
     * Display options stats.
     *
     * @return void
     */
    function display_option_table($id,$options) {

      echo '</tbody><tbody id="optiont'.$id.'" style="display:none; font-size:70%; border:solid 0.25em;">';
      echo '</tr><tr class="flexible generaltable generalbox">';
      echo '<th class="headerc c0">'.get_string('alternatives', 'quiz_psychometric').'</td>';
      echo '<th class="headerc c1">'.get_string('numberalternatives', 'quiz_psychometric').'</th>';
      echo '<th class="headerc c2">'.get_string('propalternatives', 'quiz_psychometric').'</th>';
      echo '<th class="headerc c3">'.get_string('puntaverage', 'quiz_psychometric').'</th>';
      echo '<th class="headerc c4">'.get_string('analysisalternatives', 'quiz_psychometric').'</th>';
      $rowindex = 0;

      // Options info vars
      $discr=0;
      $correctchoice=0;
      $choicesinfo = array();
      foreach ($options->choices as $o) {

        if($o->correct == 0) {
                $choicesn = array();

                if (strlen($o->answer) > 20) {
                      $opname = substr($o->answer,0,20)."...";
                } else {
                      $opname = $o->answer;
                }

                $choicesn[] = $opname;
                $choicesn[] = $this->success_average_option($o);
                $choicesinfo[] = $choicesn;
        }

        if(($o->correct == 1) && ($this->option_analysis($o,$id) < 0))
                $discr=1;

        if(($o->correct == 0) && ($this->option_analysis($o,$id) > 0))
                $discr=1;
      }
      $choicecount=0;
      foreach ($options->choices as $o) {

        if (strlen($o->answer) > 20) {
                $opname = substr($o->answer,0,20)."...";
        } else {
                $opname = $o->answer;
        }

        // Print Script for Modal Dialog
        echo '<script>
                    $(document).ready(function() {

                            $(\'#qa'.$choicecount.'-'.$id.'\').dialog({
                                                            autoOpen: false,
                                                            width:900,
                                                            height:260,
                                                            minWidth:300,
                                                            title: \''.$opname.'\',
                                                            modal: true
                                                    });
                            $(\'#bc'.$choicecount.'-'.$id.'\').click(function() {
                                                    $(\'#qa'.$choicecount.'-'.$id.'\').dialog(\'open\');
                                                    return false;
                                            });
                    });
              </script>';

        $rowindex = ($rowindex == 0) ? 1 : 0;
        echo '</tr><tr class="r'.$rowindex.'">';
        echo '<td class="cell c0"><a id="bc'.$choicecount.'-'.$id.'" href="#">'.$opname.'</a></td>';
        echo '<td class="cell c1">'.$o->number."</td>";
        echo '<td class="cell c2">'.get_float_precision($this->option_prop($o))."</td>";
        echo '<td class="cell c3">'.get_float_precision($this->success_average_option($o))."</td>";
        echo '<td class="cell c4">'.get_float_precision($this->option_analysis($o,$id))."</td>";

        // Display Hidden Modal Dialog
        echo '<div id="qa'.$choicecount.'-'.$id.'" style="display:none;">';
        if(($o->correct == 1) && ($discr == 0))
        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('dotline1', 'quiz_psychometric').'<b>'.$opname.'</b>. El n&uacute;mero de examinados que escogen esta alternativa es de <b>'.$o->number.'</b>. La proporci&oacute;n de evaluados que escogen esta alternativa es <b>'.get_float_precision($this->option_prop($o)).'</b>. '.get_string('dotline2', 'quiz_psychometric');
        echo '<div style="padding-left:20%;">';
        foreach($choicesinfo as $c) {
           echo '<img src="report/psychometric/img/rightarrowsmall.png" style="margin-right:1%;">'.get_string('dotline3', 'quiz_psychometric').'<b>';
           print_r($c[0]);
           echo '</b>'.get_string('dotline4', 'quiz_psychometric').'<b>'.$c[1].'</b><br>';
        }
        echo '</div>';
        echo '</p>';
        echo '</div>';
        $choicecount++;
      }
      echo '</tr><tr class="r'.$rowindex.'">';
      echo '<td class="cell c0" colspan="8">'.$this->display_choice_chart().'</td>';
      echo '</tbody><tbody>';
   }

     /**
     * Question Info Dialog
     *
     * @return void
     */
   function display_question_info($i,$count,$choice) {

        echo '<div id="q'.$count.'" style="display:none;">
                <p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('qiline1', 'quiz_psychometric').$count.' '.get_string('qiline2', 'quiz_psychometric').$i->choicescount.' '.get_string('qiline3', 'quiz_psychometric').'</p>
                <div style="border:2px solid; padding:2%; margin:auto; width:50%; text-align:center;">'.strtoupper($i->name);
        echo '<ul>';
        $asteriskeasy = 0;
        $asteriskhard = 0;
        $countq = 0;
        foreach($i->choices as $choice)
        {
                if($countq < $i->choicescount)
                        echo '<li>'.$choice->answer.'</li>';
                $countq++;
        }
        echo '</ul></div>
                <p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('qiline4', 'quiz_psychometric').$this->question_easily_index($i).' '.get_string('qiline5', 'quiz_psychometric').'</p>';

        if ($this->question_easily_index($i) >= 0.9) {
           echo '<center><b><a style="color:red; text-decoration:none;">'.get_string('tooeasy', 'quiz_psychometric').'</a></b></center>';
           $asteriskeasy = 1;
        }

        if (0.71 < $this->question_easily_index($i) && $this->question_easily_index($i) < 0.9) {
           echo '<center><b><a style="color:red; text-decoration:none;">'.get_string('easy', 'quiz_psychometric').'</a></b></center>';
        }

        if (0.4 < $this->question_easily_index($i) && $this->question_easily_index($i) <= 0.71) {
           echo '<center><b><a style="color:red; text-decoration:none;">'.get_string('difficult', 'quiz_psychometric').'</a></b></center>';
        }

        if (0.16 < $this->question_easily_index($i) && $this->question_easily_index($i) <= 0.4) {
           echo '<center><b><a style="color:red; text-decoration:none;">'.get_string('verydifficult', 'quiz_psychometric').'</a></b></center>';
        }

        if ($this->question_easily_index($i) <= 0.16) {
           echo '<center><b><a style="color:red; text-decoration:none;">'.get_string('toodifficult', 'quiz_psychometric').'</a></center></b></center>';
           $asteriskhard = 1;
        }
        echo '<p>'.get_string('qiline6', 'quiz_psychometric').get_float_precision($i->success * 100 / ($i->success + $i->error + $i->leftblank)).'% '.get_string('qiline7', 'quiz_psychometric').'</p>';

        if($asteriskeasy == 1) {
                echo '<div style="padding-right:3%;">';
                echo '<p>'.get_string('qiline8', 'quiz_psychometric').'</p>';
                echo '<ul style="padding-left:5%;">
                        <li>'.get_string('qiline9', 'quiz_psychometric').'</li>
                        <li>'.get_string('qiline10', 'quiz_psychometric').'</li>
                      <ul><li>'.get_string('qiline11', 'quiz_psychometric').'</li></ul>
                      </ul></div>';
        }

        if($asteriskhard == 1) {
                echo '<div style="padding-right:3%;">';
                echo '<p>'.get_string('qiline12', 'quiz_psychometric').'</p>';
                echo '<ul style="padding-left:5%;">
                        <li>'.get_string('qiline13', 'quiz_psychometric').'</li>
                        <li>'.get_string('qiline14', 'quiz_psychometric').'</li>
                        <ul><li>'.get_string('qiline15', 'quiz_psychometric').'</li></ul>
                        </ul></div>';
        }

        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('qiline16', 'quiz_psychometric').get_float_precision($this->question_standard_deviation($i)).'. '.get_string('qiline17', 'quiz_psychometric').'</p>';

        $discrimination = 0;
        if ($this->question_discrimination_index($i) >= 0.1)
                $discrimination = 1;

        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('qiline18', 'quiz_psychometric').get_float_precision($this->question_discrimination_index($i)).' '.get_string('qiline19', 'quiz_psychometric').get_float_precision($this->question_biserial_correlation($i)).'. '.get_string('qiline20', 'quiz_psychometric');

        if ($discrimination==1)
                echo '<b><a style="color:red; text-decoration:none;">'.get_string('differs', 'quiz_psychometric').'</a></b>';
        else
                echo '<b><a style="color:red; text-decoration:none;">'.get_string('nodiffers', 'quiz_psychometric').'</a></b>';

        echo get_string('qiline21', 'quiz_psychometric').'</p>';

        if ($discrimination!=1)
        {
                echo '<ul>';
                if ($this->question_discrimination_index($i) >= 0)
                                        echo '<li>'.get_string('qiline22', 'quiz_psychometric').'</li>';
                else
                                        echo '<li>'.get_string('qiline23', 'quiz_psychometric').'</li>';
                echo '</ul>';
        }

        echo '<p style="font-size:0.75em; padding-left:20px; padding-right:20px;"><i>'.get_string('qiline24', 'quiz_psychometric').'</i></p>';
        echo '<p><img src="report/psychometric/img/rightarrow.png" style="margin-right:1%;">'.get_string('qiline25', 'quiz_psychometric').get_float_precision($this->question_alpha_cronbach($i)).'. '.get_string('qiline26', 'quiz_psychometric');

        if ($this->alpha_cronbach() > $this->question_alpha_cronbach($i))
                echo get_string('qiline27', 'quiz_psychometric');
        else
                echo get_string('qiline28', 'quiz_psychometric');
        echo '</p>';

        $discrimination = 0;
        $asteriskeasy = 0;
        $asteriskhard = 0;
        echo '</div>';
    }

    /**
     * Display the question chart of the quiz.
     *
     * @return void
     */
    function display_graphics($quiz) {

        $columns = array();
        $headers = array();

        $columns[] = 'psychometricgraph';
        $headers[] = get_string('psychometricgraph', 'quiz_psychometric');
        $columns[] = 'facilitygraph';
        $headers[] = get_string('facilitygraph', 'quiz_psychometric');
        $columns[] = 'discriminationgraph';
        $headers[] = get_string('discriminationgraph', 'quiz_psychometric');

        $table = new flexible_table('mod-quiz-report-psychometric-quiz');

        $table->define_columns($columns);
        $table->define_headers($headers);

        $table->set_attribute('cellspacing', '0');
        $table->set_attribute('id', 'graphics');
        $table->set_attribute('class', 'generaltable generalbox');
        $table->set_attribute('style','text-align:center;');

        $table->setup();

        $row = array();
        $row[] = $this->display_quiz_chart($quiz);
        $row[] = $this->display_facility_chart();
        $row[] = $this->display_discrimination_chart();

        $table->add_data($row);
        $table->print_html();

    }

    /**
     * Display attempts stats.
     *
     * @return void
     */
    function display_attempts_table() {
        $columns = array();
        $headers = array();

        $columns[] = 'fullname';
        $headers[] = get_string('fullname', 'quiz_psychometric');
        $columns[] = 'nsuccess';
        $headers[] = get_string('nsuccess', 'quiz_psychometric');
        $columns[] = 'nerrors';
        $headers[] = get_string('nerrors', 'quiz_psychometric');
        $columns[] = 'nblanks';
        $headers[] = get_string('nblanks', 'quiz_psychometric');
        $columns[] = 'successpercent';
        $headers[] = get_string('successpercent', 'quiz_psychometric');
        $columns[] = 'successcorrection';
        $headers[] = get_string('successcorrection', 'quiz_psychometric');
        $columns[] = 'trustinterval';
        $headers[] = get_string('trustinterval', 'quiz_psychometric');

        $table = new flexible_table('mod-quiz-report-psychometric-attempts');

        $table->define_columns($columns);
        $table->define_headers($headers);

        $table->set_attribute('cellspacing', '0');
        $table->set_attribute('id', 'quiz');
        $table->set_attribute('class', 'generaltable generalbox');
        $table->set_attribute('style','text-align:center;');

        $table->setup();
        foreach ($this->attempts as $a) {
            $row = array();
            $row[] = $a->name;
            $row[] = $a->success;
            $row[] = $a->error;
            $row[] = $a->leftblank;
            $row[] = get_percent_precision($this->attempt_success_percent($a));
            //$row[] = get_float_precision($this->attempt_success_total($a));  // Unused column
            $row[] = get_float_precision($this->attempt_success_correction($a));
            $row[] = get_float_precision($this->attempt_trust_interval_low($a))." : ".
                     get_float_precision($this->attempt_trust_interval_high($a));

            $table->add_data($row);
        }

        echo '<h2 class="main">'.get_string('attemptstabletitle', 'quiz_psychometric').'</h2>';
        $table->print_html();
    }

    /**
     * Load data from quiz: questions, attempts and results.
     *
     * @param object  $quiz               Quiz to analyze
     * @param integer $attemptselection   Attempt policy selection
     * @param mixed   $currentgroup       false if groups not used,
     *                                    integer if groups used,
     *                                    0 means all groups
     *
     * @return string  '' if there is data to analyze, other if error
     */
    function load_data($quiz, $attemptselection = QUIZ_HIGHESTATTEMPT, $currentgroup = false, $attemptorder = '' ) {
        global $CFG, $QTYPES;

        // QUIZ_HIGHESTATTEMPT by default
        $opfield   = 'sumgrades';
        $operation = 'max('. $opfield . ')';

        switch ($attemptselection) {
            case QUIZ_FIRSTATTEMPT :
                $opfield   = 'timemodified';
                $operation = 'min('. $opfield . ')';
                break;
            case QUIZ_LASTATTEMPT :
                $opfield   = 'timemodified';
                $operation = 'max('. $opfield . ')';
                break;
        }

        if ($attemptselection == QUIZ_HIGHESTATTEMPT) {
            $sql = ' SELECT a.*, u.firstname, u.lastname '.
                   ' FROM ( '.
                   '  SELECT aux.userid, aux.sumgrades, max( aux.timemodified ) AS op2 '.
                   '  FROM ( '.
                   '   SELECT userid, max( sumgrades ) AS op1 '.
                   '   FROM '.$CFG->prefix.'quiz_attempts '.
                   '   WHERE quiz = '.$quiz->id.' AND preview = 0 '.
                   '   GROUP BY userid ) as m '.
                   '  INNER JOIN '.$CFG->prefix.'quiz_attempts AS aux '.
                   '  ON aux.userid = m.userid AND aux.sumgrades = m.op1 '.
                   '  AND aux.quiz = '.$quiz->id.' AND aux.preview = 0 '.
                   '  GROUP BY aux.userid ) AS mt '.
                   ' INNER JOIN '.$CFG->prefix.'quiz_attempts AS a '.
                   ' ON a.userid = mt.userid AND a.timemodified = mt.op2 '.
                   ' INNER JOIN '.$CFG->prefix.'user AS u '.
                   ' ON a.userid = u.id ';
        } else {
            $sql = ' SELECT a.*, u.firstname, u.lastname '.
                   ' FROM ('.
                   '  SELECT userid, '.$operation.' AS opfield '.
                   '  FROM '.$CFG->prefix.'quiz_attempts '.
                   '  WHERE quiz = '.$quiz->id.' AND preview = 0 '.
                   '  GROUP BY userid ) as m '.
                   ' INNER JOIN '.$CFG->prefix.'quiz_attempts AS a '.
                   ' ON a.userid = m.userid AND a.'.$opfield.' = m.opfield '.
                   ' AND a.quiz = '.$quiz->id.' AND a.preview = 0 '.
                   ' INNER JOIN '.$CFG->prefix.'user AS u '.
                   ' ON a.userid = u.id ';
        }

        if ($currentgroup) {
            $sql .= ' INNER JOIN '.$CFG->prefix.'groups_members AS gm '.
                    ' ON gm.groupid = '.$currentgroup.' AND a.userid = gm.userid ';
        }

        $sql .= ' ORDER BY u.lastname';

        // Fetch all attempts -> $alist
        $alist = get_records_sql($sql);

        if(empty($alist)) {
            return 'nothingtodisplay';
        }

        // Get questions list
        $qlist = quiz_questions_in_quiz($quiz->questions);
        $qlist = explode(",",$qlist);

        $this->name = $quiz->name;
        $this->attemptscount = count($alist);
        $this->questionscount    = count($qlist);


        // Load question information (only multichoice type)
        $questionlist = implode(",",$qlist);
        $sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
               "  FROM {$CFG->prefix}question q,".
               "       {$CFG->prefix}quiz_question_instances i".
               " WHERE i.quiz = '$quiz->id' AND q.id = i.question".
               "   AND q.id IN ($questionlist)".
               "   AND q.qtype = 'multichoice'";

        if (!$quizquestions = get_records_sql($sql))
        {
            return 'noquestionfound'; // 'No questions found';
        }

        // Load the question type specific information
        if (!get_question_options($quizquestions))
        {
            return 'couldnotloadquestions'; // 'Could not load question options';
        }

        // Fill question list
        foreach ($qlist as $i) {
            $question = new StdClass();

            // $qlist is the array of all question ids of the quiz
            // $quizquestions only the multichoice type questions
            // If the id is not in the $quizquestions, then ignore it.
            if (!array_key_exists($i, $quizquestions))
            {
                continue;
            }

            $question->id    = $i;
            $question->grade = $quizquestions[$i]->maxgrade;
            if (strlen($quizquestions[$i]->name) > 20) {
                $question->name = substr($quizquestions[$i]->name,0,20)."...";
            } else {
                $question->name = $quizquestions[$i]->name;
            }

            $question->success   = 0;
            $question->error     = 0;
            $question->leftblank = 0;
            $question->easilyindex         = "Nc";
            $question->discriminationindex = "Nc";
            $question->variation           = "Nc";
            $question->standarddeviation   = "Nc";
            $question->biserialcorrelation = "Nc";
            $question->alphacronbach       = "Nc";
            $question->quizdeviationwithoutme   = "Nc";
            $question->quizsuccessavgwithoutme  = "Nc";

            // Load available choices per question
            $question->choices = array();

            $q = get_question_responses($quizquestions[$i], null);

            foreach ($q->responses as $key => $r) {

               $choice = new StdClass();
               $choice->id = $key;
               $choice->answer = $r->answer;
               $choice->credit = $r->credit;
               $choice->number = 0;
               $choice->users = array();
               $choice->correct = 0;
               $question->choices[$key] = $choice;
            }
            $question->choicescount  = count($question->choices);

            // Add a 'last' choice to calculate not answered items
            $choice = new StdClass();
            $choice->id = 0;
            $choice->answer = get_string('noselected', 'quiz_psychometric');
            $choice->credit = 0;
            $choice->number = 0;
            $choice->users = array();
            $choice->correct = 0;
            //$question->choices[++$key] = $choice;
            $question->choices[0] = $choice;

            $this->questions[$question->id] = $question;
        }

        // Rewiew all attempts and record data
        foreach ($alist as $a) {
            $attempt = new StdClass();

            $attempt->id        = $a->id;
            $attempt->userid    = $a->userid;
            if (!empty($a->firstname)) {
                $attempt->name  = $a->firstname;
                if (!empty($a->lastname)) {
                    $attempt->name .= " ".$a->lastname;
                }
            } else {
                $attempt->name  = $a->lastname;
            }
            $attempt->score     = $a->sumgrades;
            $attempt->success   = 0;
            $attempt->error     = 0;
            $attempt->leftblank = 0;

            $attempt->successpercent    = "Nc";
            $attempt->successtotal      = "Nc";
            $attempt->successcorrection = "Nc";
            $attempt->trustintervallow  = "Nc";
            $attempt->trustintervalhigh = "Nc";

            $attempt->successquestions   = array();
            $attempt->errorquestions     = array();
            $attempt->leftblankquestions = array();

            $this->attempts[$attempt->id] = $attempt;

            // Restore the question sessions to their most recent states
            // Creating new sessions where required
            if (!$states = get_question_states($quizquestions, $quiz, $a)) {
                return 'couldnotrestorequestion'; // 'Could not restore question sessions';
            }

            foreach ($qlist as $i) {
                if (!isset($quizquestions[$i])) {
                    // No question instance, that's not possible
                    continue;
                }
                if (!isset($states[$i])) {
                    // No answer for this question in this attempt, leftblank
                    $this->questions[$i]->leftblank++;
                    $this->attempts[$a->id]->leftblank++;
                    $this->attempts[$a->id]->leftblankquestions[$i] = "blank";
                    continue;
                }

                $answers = get_question_actual_response($quizquestions[$i], $states[$i]);

                $corrects = $QTYPES[$quizquestions[$i]->qtype]->get_correct_responses($quizquestions[$i], $states[$i]);

                // $corrects is an array but we support only one correct answer
                // per multichoice question, so get the first element
                $correct = current($corrects);
                $correctanswer = $this->questions[$i]->choices[$correct]->answer;

                if (!empty($answers)) {
                    // $answers is an array but we support only one answer
                    // per multichoice question, so get the first element
                    $answer = current($answers);

                    foreach($states[$i]->responses as $optionmarked);

                    //$optionmarked =
                    $this->questions[$i]->choices[$optionmarked]->number++;
                    $this->questions[$i]->choices[$optionmarked]->users[] = $a->userid;

                    // Is this answer correct?
                    if ($answer == $correctanswer) {
                        // Success
                        $this->questions[$i]->choices[$optionmarked]->correct = 1;
                        $this->questions[$i]->success++;
                        $this->attempts[$a->id]->success++;
                        $this->attempts[$a->id]->successquestions[$i] = $answer;
                    } else {
                        // Error
                        $this->questions[$i]->error++;
                        $this->attempts[$a->id]->error++;
                        $this->attempts[$a->id]->errorquestions[$i] = $answer;
                    }
                } else {
                    // No answer in this attempt for this question, leftblank
                    $this->questions[$i]->choices[0]->number++;
                    $this->questions[$i]->choices[0]->users[] = $a->userid;
                    $this->questions[$i]->leftblank++;
                    $this->attempts[$a->id]->leftblank++;
                    $this->attempts[$a->id]->leftblankquestions[$i] = "blank";
                }
            }
        }

        return '';
    }

    /**
     * Calculate the average of the number of choices of all questions.
     * 'success_correction' stat needs it.
     *
     * @return float      Choices count average of the quiz
     */
    function choices_count_average() {
        $cca = 0;
        if (isset($this->questions) &&
            is_array($this->questions) && ($this->questionscount > 0)) {
            if (!is_numeric($this->choicescountavg)) {
                $sum = 0;
                foreach ($this->questions as $i) {
                         $sum += $i->choicescount;
                }
                $cca = $sum / $this->questionscount;
                $this->choicescountavg = $cca;
            } else {
                $cca = $this->choicescountavg;
            }
        }
        return $cca;
    }

    /**
     * Calculate the average of the number of success answers
     * of all questions in all selected attempts of selected students.
     *
     * @param object $options Question to analyze
     *
     * @return float      Success average of the quiz
     */
    function success_average_option($options) {

       $sum = 0;

        if (isset($this->attempts) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {

            foreach($options->users as $u)
            {

                foreach($this->attempts as $a)
                {
                        if($u==$a->userid)
                        {
                                $sum+=$a->score;
                        }
                }
            }
            return $sum / $this->attemptscount;
        }

    }

    /**
     * Calculate the average of the number of success answers
     * of all questions in all selected attempts.
     * 'standard_deviation' stat needs it.
     *
     * @return float      Success average of the quiz
     */
    function success_average() {
        $sa = 0;
        if (isset($this->attempts) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {
            if (!is_numeric($this->successavg)) {
                $sum = 0;
                foreach ($this->attempts as $a) {
                    $sum += $a->success;
                }
                $sa = $sum / $this->attemptscount;
                $this->successavg = $sa;
            } else {
                $sa = $this->successavg;
            }
        }
        return $sa;
    }

    /**
     * Calculate the average of a test if a question is removed
     * and counting rest of questions in all selected attempts.
     *
     * @return float      Average of the quiz
     */
    function test_average_without_question($question) {
        $sa = 0;
        if (isset($this->attempts) && isset($question) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {
            if (!is_numeric($question->quizsuccessavgwithoutme)) {
                $sum = 0;
                foreach ($this->attempts as $a)
                {
                    $sum += $a->success;
                    if (array_key_exists($question->id, $a->successquestions)) {
                        $sum -= $question->grade;
                    }
                }
                $sa = $sum / $this->attemptscount;
                //echo 'Avarage_without_Item='.$sum.'/'.$this->attemptscount.'='.$sa.'<br>'; // Debug
                $question->quizsuccessavgwithoutme = $sa;
            } else {
                $sa = $question->quizsuccessavgwithoutme;
            }
        }
        return $sa;
    }

    /**
     * Calculate the average of the number of success answers
     * of all questions in all selected attempts, respect the number
     * of choices of the question.
     *
     * @return float      Success correction of the quiz
     */
    function success_correction() {
        $sc = 0;
        if (isset($this->attempts) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {
            if (!is_numeric($this->successcorrection)) {
                // If number of choices if less than 2
                // then this quiz is bad-built
                if ($this->choices_count_average() > 1) {
                    $sum = 0;
                    foreach ($this->attempts as $a) {
                        $error = 1 / ($this->choices_count_average() - 1);
                        $error = $error * $a->error;
                        $sumquestion = $a->success - $error;
                        $sum += $sumquestion;
                    }
                    $sc = $sum / $this->attemptscount;
                } else {
                    $sc = "Na";
                }
                $this->successcorrection = $sc;
            } else {
                $sc = $this->successcorrection;
            }
        }
        return $sc;
    }

    /**
     * Calculate the variation of the quiz.
     *
     * @return float      Variation of the quiz
     */
    function variation() {
        $v = 0;
        if (isset($this->attempts) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {
            if (!is_numeric($this->variation)) {
                $sum = 0;
                foreach ($this->attempts as $a) {
                    $sumquestion = $a->success - $this->success_average();
                    $sumquestion = pow($sumquestion, 2);
                    $sum += $sumquestion;
                }
                $v = $sum / $this->attemptscount;
                $this->variation = $v;
            } else {
                $v = $this->variation;
            }
        }
        return $v;
    }

    /**
     * Calculate the standard deviation of the quiz.
     *
     * @return float      Standard deviation of the quiz
     */
    function standard_deviation() {
        $sd = 0;
        if (!is_numeric($this->standarddeviation)) {
            $sd = sqrt($this->variation());
            $this->standarddeviation = $sd;
        } else {
            $sd = $this->standarddeviation;
        }
        return $sd;
    }

    /**
     * Calculate the deviation of a test if a question is removed
     *
     * @return float      Standard deviation of the quiz if question is removed from test
     */
    function test_deviation_without_question($question) {
        $v = 0;
        if (isset($this->attempts) && isset($question) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {
            if (!is_numeric($question->quizdeviationwithoutme)) {
                $aver = $this->test_average_without_question($question);
                $sum = 0;
                foreach ($this->attempts as $a)
                {
                    $sumquestion = $a->success;
                    if (array_key_exists($question->id, $a->successquestions)) {
                        $sumquestion -= $question->grade;
                    }
                    $sum += ($sumquestion - $aver) * ($sumquestion - $aver);
                }
                $v = sqrt( $sum / $this->attemptscount );
                $question->quizdeviationwithoutme = $v;
            } else {
                $v = $question->quizdeviationwithoutme;
            }
        }
        return $v;
    }

    /**
     * Calculate the biserial correlation of the quiz.
     *
     * @return float      Biserial correlation of the quiz
     */
    function biserial_correlation() {
        $bc = 0;
        if (isset($this->questions) &&
            is_array($this->questions) && ($this->questionscount > 0)) {
            if (!is_numeric($this->biserialcorrelation)) {
                $sum = 0;
                foreach ($this->questions as $q) {
                    if (is_numeric($this->question_biserial_correlation($q))) {
                        $sum += $this->question_biserial_correlation($q);
                    }
                }
                $bc = $sum / $this->questionscount;
                $this->biserialcorrelation = $bc;
            } else {
                $bc = $this->biserialcorrelation;
            }
        }
        return $bc;
    }

    /**
     * Calculate the success average proportion of the quiz.
     *
     * @return float      Success average proportion of the quiz
     */
    function success_average_proportion() {
        $sap = 0;
        if ($this->questionscount > 0) {
            if (!is_numeric($this->successavgproportion)) {
                // If we do not known the score average
                // then calculate it
                if (!is_numeric($this->successavg)) {
                    $this->successavg = $this->success_average();
                }
                $sap = $this->successavg / $this->questionscount;
                $this->successavgproportion = $sap;
            } else {
                $sap = $this->successavgproportion;
            }
        }
        return $sap;
    }

    /**
     * Calculate the Cronbach alpha of the quiz.
     *
     * @return float      Cronbach alpha of the quiz
     */
    function alpha_cronbach() {
        $ac = 0;
        if (isset($this->questions) &&
            is_array($this->questions) && ($this->questionscount > 1) &&
            is_numeric($this->variation()) && ($this->variation() > 0)) {
            if (!is_numeric($this->alphacronbach)) {
                $sum = 0;
                foreach ($this->questions as $q) {
                    if (is_numeric($this->question_deviation($q))) {
                        $sum += $this->question_deviation($q);
                    }
                }
                $ac = 1 - ($sum / $this->variation());
                $ac = ($this->questionscount / ($this->questionscount - 1)) * $ac;
                $this->alphacronbach = $ac;
                //echo 'Alfa = '.$ac.'<br>';  // Debug
            } else {
                $ac = $this->alphacronbach;
            }
        }
        return $ac;
    }

    /**
     * Select the x percent best or worst attempts.
     *
     * @param  integer  $cutpercent  Cut percent
     * @param  string   $selection  'BEST' or 'WORST'
     *
     * @return array      Array of selected attempts
     */
    function select_attempts($cutpercent, $selection = 'BEST'){
        $sa = array();
        if (isset($this->attempts) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {
            $aa = $this->attempts;
            if ($selection == 'BEST') {
                uasort($aa,'attempt_compare_descending');
            } else {
                uasort($aa,'attempt_compare_ascending');
            }
            $cut = $cutpercent * $this->attemptscount;
            //$cut = intval(round($cut / 100));  // Round-down
            $cut = intval(ceil($cut / 100));  // Round-up to match with TAP calculus
            $sa = array_slice ($aa, 0, $cut, true);
        }
        return $sa;
    }

    /**
     * Select the x percent best attempts.
     *
     * @param  integer $cutpercent   Cut percent
     *
     * @return array      Array of best attempts
     */
    function best_attempts($cutpercent){
        return $this->select_attempts($cutpercent, 'BEST');
    }

    /**
     * Select the x percent worst attempts.
     *
     * @param  integer $cutpercent   Cut percent
     *
     * @return array      Array of worst attempts
     */
    function worst_attempts($cutpercent){
        return $this->select_attempts($cutpercent, 'WORST');
    }

    /**
     * Calculate the question easily index in this quiz.
     *
     * @param  object $question   Question to analyze
     *
     * @return float     Easily index of the question
     */
    function question_easily_index($question) {
        $ei = 0;
        if (isset($question)) {
            if (!is_numeric($question->easilyindex)) {
                //$qa = $question->success + $question->error; // Count only success and error (no omissions)
                $qa = $this->attemptscount;  // Count all tries
                if ($qa > 0) {
                    $ei = $question->success / $qa;
                }
                $question->easilyindex = $ei;
            } else {
                $ei = $question->easilyindex;
            }
        }
        return $ei;
    }

    /**
     * Calculate the question discrimination index in this quiz.
     *
     * @param  object $question   Question to analyze
     *
     * @return float     Discrimination index of the question
     */
    function question_discrimination_index($question) {
        $di = 0;
        if (isset($question)) {
            if (!is_numeric($question->discriminationindex)) {
                $basp = attempts_success_proportion($this->best_attempts(QUIZ_REPORT_CUTPERCENT), $question);
                $wasp = attempts_success_proportion($this->worst_attempts(QUIZ_REPORT_CUTPERCENT), $question);
                $di = $basp - $wasp;
                $question->discriminationindex = $di;
            } else {
                $di = $question->discriminationindex;
            }
        }
        return $di;
    }

    /**
     * Calculate the question deviation in this quiz.
     *
     * @param  object $question   Question to analyze
     *
     * @return float     Standard deviation of the question (item)
     */
    function question_deviation($question) {
        $v = 0;
        if (isset($question)) {
            if (!is_numeric($question->variation)) {
                //$ac = $question->success + $question->error;  // Without omissions
                $ac = $this->attemptscount;  // With omissions
                $ec = $ac - $question->success;  // Errors (omissions included)
                $sa = 0;
                if( $ac > 0 )
                    $sa = $question->success / $this->attemptscount;
                $sum = pow((1 - $sa), 2) * $question->success;
                //$sum = $sum + (pow((0 - $sa), 2) * $question->error);
                $sum = $sum + (pow((0 - $sa), 2) * $ec);
                $v = $sum / $ac;
                $question->variation = $v;
            } else {
                $v = $question->variation;
            }
        }
        return $v;
    }


    /**
     * Calculate the comprehensive analysis of each choice
     *
     * @param  object $option   Choice to analyze
     *
     * @return float     Percentage of students
     */
     function option_analysis($options,$id) {

        // Succes Average Yes/No
        $say = 0;
        $san = 0;
        $students = array();

        //First, success average without this question of students that make this choice
        foreach($options->users as $u)
      {
        foreach($this->attempts as $a)
        {
                if($u==$a->userid)
                {
                  if ($optiones->correct == 1)
                                        $say += $a->success - 1;
                        else
                                        $say += $a->success;
                }
        }
      }

        //Second, success average without this question of students don't make this choice
        if(!empty($options->users) && $options->id!=0)
          foreach($this->attempts as $a)
                {
                        if(!in_array($a->userid,$options->users) && !in_array($u,$students))
                        {
                                $students[] = $a->userid;
                        }
                }

     foreach($students as $u)
      {
        foreach($this->attempts as $a)
        {
                if($u==$a->userid)
                {
                                        $san += $a->success;
                }
        }
      }

      $dsv = 0;
      $num = 0;
      $count = 0;
      //Third, Tipical desviation without this question
     foreach($this->attempts as $a)
     {
        $count ++;
        if ($optiones->correct == 1)
                $num += pow (($a->success - 1 - $this->success_average()),2);
        else
          $num += pow (($a->success - $this->success_average()),2);

    }
     $dsv = sqrt ( $num / $count );

      //Fourth, the right operator

      $rop = sqrt ( $this->option_prop($options) / (1 - $this->option_prop($options)));


        return ( (($say - $san) / $dsv ) * $rop);
    }


    /**
     * Calculate the percentage of students who make that choice
     *
     * @param  object $option   Choice to analyze
     *
     * @return float     Percentage of students
     */
     function option_prop($option) {
        if (isset($this->attempts) &&
            is_array($this->attempts) && ($this->attemptscount > 0)) {
                                return $option->number / $this->attemptscount;
                        }
    }

    /**
     * Calculate the question standard deviation in this quiz.
     *
     * @param  object $question   Question to analyze
     *
     * @return float     Standard deviation of the question
     */
    function question_standard_deviation($question) {
        $sd = 0;
        if (isset($question)) {
            if (!is_numeric($question->standarddeviation)) {
                $sd = sqrt($this->question_deviation($question));
                $question->standarddeviation = $sd;
            } else {
                $sd = $question->standarddeviation;
            }
        }
        return $sd;
    }

    /**
     * Calculate the question biserial correlation in this quiz.
     *
     * @param  object $question   Question to analyze
     *
     * @return float     Biserial correlation of the question
     */
    function question_biserial_correlation($question) {
        $bc = 0;
        $avgwm = 0;
        $avgwom = 0;
        $propsuccess = 0;
        $properror = 0;
                  $total = 0;
                  $noproc = 0;
                  $count = 0;
                  $counterr = 0;
          $sum = 0;
          $sumerr = 0;

          foreach ($this->attempts as $a)
          {
            if (array_key_exists($question->id, $a->successquestions))
            {
                  $sum += $a->success - $question->grade;
                  $count++;
            }
            else
            {
                  $sumerr += $a->success;
                  $counterr++;
            }
        }

          if($count == 0 || $counterr==0)
                        $noproc = 1;

          $avgwm   = $sum / $count;
          $avgwom  = $sumerr / $counterr;
          $stddesv = $this->test_deviation_without_question($question);
          //echo 'Std_Deviation_without_Item = '.$stddesv.'<br>';  // Debug

          // Prop success / fail

          //$total = $question->success + $question->error + $question->leftblank;
          $total = $count + $counterr;
          $propsuccess = $count / $total;
          $properror = $counterr / $total;

          // Biserial correlation

          $bc = ( ($avgwm - $avgwom) / $stddesv ) * sqrt ($propsuccess * $properror);
        //echo 'Rpb_Item = ('.$avgwm.'-'.$avgwom.'/'.$stddesv.') x SQRT('.$propsuccess.'x'.$properror.') = '.$bc.'<br>';  // Debug

                if (    $noproc == 1 )
                        $bc = '-';

        return $bc;
    }

    /**
     * Calculate the question Cronbach alpha in this quiz.
     *
     * @param  object $question   Question to analyze
     *
     * @return float     Cronbach alpha of the question
     */
    function question_alpha_cronbach($question) {
        $ac = 0;
        $qdeviation = $this->test_deviation_without_question($question);
        if( isset($this->questions) && is_array($this->questions) && ($this->questionscount > 2) &&
            is_numeric($qdeviation) && ($qdeviation > 0) )
        {
            if (!is_numeric($question->alphacronbach)) {
                $sum = 0;
                foreach ($this->questions as $q)
                {
                    if ($q->id != $question->id)
                    {
                        $ideviation = $this->question_standard_deviation($q);
                        if (is_numeric($ideviation)) {
                            $ideviation = $ideviation * $ideviation;
                            $sum += $ideviation;
                        }
                    }
                }
                $qdeviation = $qdeviation * $qdeviation;
                $ac = 1 - ($sum / $qdeviation);
                $ac = (($this->questionscount - 1) / ($this->questionscount - 2)) * $ac;
                $question->alphacronbach = $ac;
            } else {
                $ac = $question->alphacronbach;
            }

        }
        return $ac;
    }

    /**
     * Calculate the attempt success percent.
     *
     * @param  object $attempt   Attempt to analyze
     *
     * @return float     Success percent of the attempt
     */
    function attempt_success_percent($attempt) {
        $sp = 0;
        if (isset($attempt) && ($this->questionscount > 0)) {
            if (!is_numeric($attempt->successpercent)) {
                $sp = $attempt->success / $this->questionscount;
                $attempt->successpercent = $sp;
            } else {
                $sp = $attempt->successpercent;
            }
        }
        return $sp;
    }

    /**
     * Calculate the attempt success total.
     *
     * @param  object $attempt   Attempt to analyze
     *
     * @return float     Success total of the attempt
     */
    function attempt_success_total($attempt) {
        $st = 0;
        if (isset($attempt)) {
            if (!is_numeric($attempt->successtotal)) {
                $st = $attempt->success;
                $attempt->successtotal = $st;
            } else {
                $st = $attempt->successtotal;
            }
        }
        return $st;
    }

    /**
     * Calculate the attempt success correction.
     *
     * @param  object $attempt   Attempt to analyze
     *
     * @return float     Success correction of the attempt
     */
    function attempt_success_correction($attempt) {
        $sc = 0;
        if (isset($attempt) && ($this->choices_count_average() > 1)) {
            if (!is_numeric($attempt->successcorrection)) {
                $sc = (1 / ($this->choices_count_average() - 1)) * $attempt->error;
                $sc = $attempt->success - $sc;
                $attempt->successcorrection = $sc;
            } else {
                $sc = $attempt->successcorrection;
            }
        }
        return $sc;
    }

    /**
     * Calculate the attempt trust interval.
     *
     * @param  object $attempt   Attempt to analyze
     *
     * @return float     Trust interval of the attempt
     */
    function attempt_trust_interval($attempt, $low = true) {
        $ti = 0;
        if (isset($attempt)) {
            if ($low) {
                $ti = $attempt->success - (QUIZ_REPORT_TRUSTINTERVAL * $this->standard_deviation() * sqrt(1 - $this->alpha_cronbach()) );
            } else {
                $ti = $attempt->success + (QUIZ_REPORT_TRUSTINTERVAL * $this->standard_deviation() * sqrt(1 - $this->alpha_cronbach()) );
            }
        }
        return $ti;
    }

    /**
     * Calculate the attempt trust interval.
     *
     * @param  object $attempt   Attempt to analyze
     *
     * @return float     Trust interval of the attempt
     */
    function attempt_trust_interval_low($attempt) {
        $ti = 0;
        if (isset($attempt)) {
            if (!is_numeric($attempt->trustintervallow)) {
                $ti = $this->attempt_trust_interval($attempt, true);
                $attempt->trustintervallow = $ti;
            } else {
                $ti = $attempt->trustintervallow;
            }
        }
        return $ti;
    }

    /**
     * Calculate the attempt trust interval.
     *
     * @param  object $attempt   Attempt to analyze
     *
     * @return float     Trust interval of the attempt
     */
    function attempt_trust_interval_high($attempt) {
        $ti = 0;
        if (isset($attempt)) {
            if (!is_numeric($attempt->trustintervalhigh)) {
                $ti = $this->attempt_trust_interval($attempt, false);
                $attempt->trustintervalhigh = $ti;
            } else {
                $ti = $attempt->trustintervalhigh;
            }
        }
        return $ti;
    }
}

/**
 * Will sort elements with less success and more errors first.
 * Callback fuction uses in uasort()
 *
 * @param  object $a    Attempt to compare
 * @param  object $b    Attempt to compare
 *
 * @return integer      0 if equal,
 *                      1 if $a is better than $b,
 *                     -1 if $a is worse than $b,
 */
function attempt_compare($a, $b) {
    $retval = 0;
    if (isset($a) && isset($b)) {
        if ($a->success == $b->success) {
            if ($a->error == $b->error) {
                $retval = 0;
            } else {
                $retval = ($a->error > $b->error) ? -1 : 1;
            }
        } else {
            $retval = ($a->success < $b->success) ? -1 : 1;
        }
    }
    return $retval;
}

/**
 * Will sort elements with less success and more errors first.
 * Callback fuction uses in uasort()
 *
 * @param  object $a    Attempt to compare
 * @param  object $b    Attempt to compare
 *
 * @return integer      0 if equal,
 *                      1 if $a is better than $b,
 *                     -1 if $a is worse than $b,
 */
function attempt_compare_ascending($a, $b) {
    return attempt_compare($a, $b);
}

/**
 * Will sort elements with more success and less errors first.
 * Callback fuction uses in uasort()
 *
 * @param  object $a    Attempt to compare
 * @param  object $b    Attempt to compare
 *
 * @return integer      0 if equal,
 *                      1 if $a is worse than $b,
 *                     -1 if $a is better than $b,
 */
function attempt_compare_descending($a, $b) {
    return -(attempt_compare($a, $b));
}

/**
 * Calculate the success proportion of a set of attempts for a question.
 *
 * @param  array  $attempts    Set of attempts
 * @param  object $question    Question
 *
 * @return float      Success proportion
 */
function attempts_success_proportion($attempts, $question) {
    $asp = 0;
    if (isset($attempts) && isset($question) && is_array($attempts)) {
        $acount = count($attempts);
        if ($acount > 0) {
            $sum = 0;
            foreach ($attempts as $a) {
                if (array_key_exists($question->id, $a->successquestions)) {
                    $sum ++;
                }
            }
            $asp = $sum / $acount;
        }
    }
    return $asp;
}

/**
 * Return a float number with specific decimal precicion.
 *
 * @param  float   $a    Value
 * @param  integer $b    Precicion
 *
 * @return float   $value rounded to $precision number of decimals,
 *                 if $value is not numeric return it without change
 */
function get_float_precision($value, $precision = QUIZ_REPORT_PRECISION) {
    if (is_numeric($value)){
        return number_format($value, $precision);
    } else {
        return $value;
    }
}

/**
 * Return a float number with specific decimal precicion in percent format.
 *
 * @param  float   $a    Value
 * @param  integer $b    Precicion
 *
 * @return string  $value rounded to $precision number of decimals
 *                 in percent format,
 *                 if $value is not numeric return it without change
 */
function get_percent_precision($value, $precision = QUIZ_REPORT_PRECISION) {
    if (is_numeric($value)){
        return round($value * 100, $precision)."%";
    } else {
        return $value;
    }
}

?>
